#include "DeskNotes.h"

///Copyright (c) 2009-2010 T. Klepzig 

BOOL DeskNotes::dwmEnabled;

DeskNotes::DeskNotes()
{
	width=300;
	height=270;
	minWidth=200;
	minHeight=200;
	dwmMarginSize=5;
	
	topMost = false;
	transparentOnLeave = true;
	shrunken = false;
	restoredHeight = 0;	
}

DeskNotes::~DeskNotes()
{
	Deactivate();

	int zoomNumerator = 0;
	int zoomDenominator = 0;
	SendMessage(editHWnd, EM_GETZOOM, (WPARAM)&zoomNumerator, (LPARAM)&zoomDenominator);

	POINT scrollPosition = {0};
	SendMessage(editHWnd, EM_GETSCROLLPOS, 0, (LPARAM)&scrollPosition);

	Settings settings;
	Settings::Data data={0};
	settings.Load(&data);
	data.DeskNotesData.DeskNoteBounds = this->GetBounds();
	data.DeskNotesData.Shrunken = this->shrunken;
	data.DeskNotesData.RestoredHeight = this->restoredHeight;
	data.DeskNotesData.TopMost = this->topMost;
	data.DeskNotesData.TransparentOnLeave = this->transparentOnLeave;
	data.DeskNotesData.ZoomNumerator = zoomNumerator;
	data.DeskNotesData.ZoomDenominator = zoomDenominator;
	data.DeskNotesData.ScrollPosition = scrollPosition;
	settings.Save(data);

	DestroyMenu(systemMenu);
	DestroyMenu(editMenu);
}

void DeskNotes::Create(HINSTANCE hInstance)
{
	OSVERSIONINFO osvi={0};
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	GetVersionEx(&osvi);

	if (osvi.dwMajorVersion < 6)
		dwmEnabled = FALSE;
	else
		DwmIsCompositionEnabled(&dwmEnabled);

	if (dwmEnabled)
		roundCornerValue = 15;
	else
		roundCornerValue = 0;	
	
	Settings settings;
	Settings::Data data={0};
	data.DeskNotesData.Shrunken = false;
	data.DeskNotesData.RestoredHeight = 0;
	data.DeskNotesData.TopMost = false;
	data.DeskNotesData.TransparentOnLeave = true;
	data.DeskNotesData.ZoomNumerator = 0;
	data.DeskNotesData.ZoomDenominator = 0;
	settings.Load(&data);
	
	RECT clientRect = {0,0,width,height};

	int x = CW_USEDEFAULT;
	int y = CW_USEDEFAULT;

	if (RECTWIDTH(data.DeskNotesData.DeskNoteBounds) > 0 && RECTHEIGHT(data.DeskNotesData.DeskNoteBounds) > 0)
	{
		x = data.DeskNotesData.DeskNoteBounds.left;
		y = data.DeskNotesData.DeskNoteBounds.top;
	}

	if (RECTWIDTH(data.DeskNotesData.DeskNoteBounds) > 0 && RECTHEIGHT(data.DeskNotesData.DeskNoteBounds) > 0)
	{
		width = RECTWIDTH(data.DeskNotesData.DeskNoteBounds);
		height = RECTHEIGHT(data.DeskNotesData.DeskNoteBounds);
	}
	else
	{
		AdjustWindowRectEx(&clientRect,WS_CAPTION|WS_THICKFRAME,0,WS_EX_TOOLWINDOW);
		width = RECTWIDTH(clientRect);
		height = RECTHEIGHT(clientRect);
	}

	this->shrunken = data.DeskNotesData.Shrunken;
	this->restoredHeight = data.DeskNotesData.RestoredHeight;
	this->topMost = data.DeskNotesData.TopMost;
	this->transparentOnLeave = data.DeskNotesData.TransparentOnLeave;

	Window::Create(hInstance, TEXT("DeskNotesWindow"), WndProc, WS_EX_LAYERED | WS_EX_TOOLWINDOW, WS_THICKFRAME, CS_VREDRAW | CS_HREDRAW | CS_NOCLOSE, GetStockBrush(BLACK_BRUSH), 0, LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)));
	this->SetText(Translate(TEXT("Notes")));
	this->SetSize(width, height, true);
	this->SetLocation(x, y);
	this->SetTopMost(this->topMost);

	LoadLibrary(TEXT("Msftedit.dll"));
	editHWnd = CreateWindowEx(0, MSFTEDIT_CLASS, 0, WS_VISIBLE | ES_MULTILINE | WS_CHILD | WS_TABSTOP | ES_AUTOVSCROLL | WS_VSCROLL, dwmMarginSize, dwmMarginSize, width - 2 * dwmMarginSize, height - 2 * dwmMarginSize, this->GetHWnd(), 0, hInstance, 0);

	systemMenu = CreatePopupMenu();
	AppendMenu(systemMenu, topMost ? MF_STRING | MF_CHECKED : MF_STRING, MenuSystem_TopMost,  Translate(TEXT("TopMost")).c_str());
	AppendMenu(systemMenu,  transparentOnLeave ? MF_STRING | MF_CHECKED : MF_STRING, MenuSystem_TransparentOnLeave, Translate(TEXT("Transparent On Leave")).c_str());
	AppendMenu(systemMenu, MF_SEPARATOR, MenuSystem_Separator1, 0);
	AppendMenu(systemMenu, MF_STRING, MenuSystem_ShrinkRestore, this->shrunken ? Translate(TEXT("Restore")).c_str() : Translate(TEXT("Shrink")).c_str());

	editMenu = CreatePopupMenu();
	AppendMenu(editMenu, MF_STRING, MenuEdit_Save, Translate(4, TEXT("Save"), TEXT("#\t"), TEXT("Ctrl"), TEXT("#+S")).c_str());
	AppendMenu(editMenu, MF_SEPARATOR, MenuEdit_Separator1, 0);
	AppendMenu(editMenu, MF_STRING, MenuEdit_ResetZoom, Translate(TEXT("Reset Zoom")).c_str());
	
	//EN_LINK aktivieren
	SendMessage(editHWnd,EM_SETEVENTMASK, 0, ENM_SELCHANGE | ENM_LINK | ENM_KEYEVENTS | ENM_CHANGE | ENM_DRAGDROPDONE);
	
	//Set the control to automatically detect URLs
	SendMessage(editHWnd, EM_AUTOURLDETECT, TRUE, 0);

	//RTF BGColor
	SendMessage(editHWnd,EM_SETBKGNDCOLOR,0,RGB(255,255,255));

	//Change Font
	CHARFORMAT2 cf;
	cf.cbSize=sizeof(CHARFORMAT2);
	cf.dwMask=CFM_FACE|CFM_SIZE;
	cf.yHeight = 220;
	lstrcpy(cf.szFaceName, TEXT("Segoe Print"));
	SendMessage(editHWnd,EM_SETCHARFORMAT,0,(LPARAM)&cf);
	
	BOOL bl=TRUE;
	DwmSetWindowAttribute(this->GetHWnd(),DWMWA_DISALLOW_PEEK,&bl, sizeof(bl));
	DwmSetWindowAttribute(this->GetHWnd(),DWMWA_EXCLUDED_FROM_PEEK,&bl, sizeof(bl));

	DWMFLIP3DWINDOWPOLICY flip3DPolicy = DWMFLIP3D_EXCLUDEBELOW;
	DwmSetWindowAttribute(this->GetHWnd(), DWMWA_FLIP3D_POLICY, &flip3DPolicy, sizeof(DWMWA_FLIP3D_POLICY));

	Shrink(shrunken);
}


CCState DeskNotes::Activate()
{
	CCState result;
	if ((result = CassiopeiaComponent::Activate()) != CCState::Successful)
		return result;

	//create directory for DeskNotes if necessary
	TCHAR currentDir[_MAX_PATH] = {0};
	TCHAR directory[_MAX_PATH]={0};
	GetCurrentDirectory(sizeof(currentDir),currentDir);
    _stprintf(directory, TEXT("%s\\DeskNotes Archives"), currentDir);
	CreateDirectory(directory, 0);

	MARGINS margins = {dwmMarginSize,dwmMarginSize,dwmMarginSize,dwmMarginSize};
	DwmExtendFrameIntoClientArea(this->GetHWnd(),&margins);

	if (!this->shrunken && this->transparentOnLeave)
		SetLayeredWindowAttributes(this->GetHWnd(), 0, 120, LWA_ALPHA);
	else
		SetLayeredWindowAttributes(this->GetHWnd(), 0, 255, LWA_ALPHA);
	
	Utilities::MoveWindowIntoVisibleArea(this->GetHWnd());
	
	this->ShowNoActivate();

	if (!this->topMost)
		SetWindowPos(this->GetHWnd(), Utilities::GetLastValidTopLevelWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

	LoadDeskNote();
	LoadRichEditConfiguration();

	RegisterHotKey(this->GetHWnd(),1,MOD_WIN,78);
	SetTimer(this->GetHWnd(),23,1000*60*2,TimerProc);

	return CCState::Successful;
}

CCState DeskNotes::Deactivate()
{
	if (!active)
		return CCState::NoActionNecessary;;

	if (!SaveDeskNote())
	{
		if (MessageBox(0,Translate(TEXT("Saving DeskNote failed. Continue without saving?")).c_str(),Translate(TEXT("Error")).c_str(),MB_YESNO) == IDNO)
			return CCState::NotReady;
	}

	CCState result;
	if ((result = CassiopeiaComponent::Deactivate()) != CCState::Successful)
		return result;

	this->Hide();
	KillTimer(this->GetHWnd(),23);
	UnregisterHotKey(this->GetHWnd(),1);	

	return CCState::Successful;
}


tstring DeskNotes::GetDeskNoteArchivesFileName()
{
	SYSTEMTIME time;
    GetSystemTime(&time);
    TCHAR fileName[_MAX_PATH]={0};
    _stprintf(fileName, TEXT("DeskNotes Archives\\DeskNote_%d-%02d-%02d.rtf"), time.wYear, time.wMonth, time.wDay);
	tstring result(fileName);

	return result;
}

tstring DeskNotes::GetDeskNoteFileName()
{
	return TEXT("DeskNote.rtf");
}

bool DeskNotes::SaveDeskNote()
{
	if (!SaveRichEditToFile(editHWnd,GetDeskNoteArchivesFileName().c_str()))
		return false;

	return SaveRichEditToFile(editHWnd,GetDeskNoteFileName().c_str());
}

bool DeskNotes::LoadDeskNote()
{
	return FillRichEditFromFile(editHWnd, GetDeskNoteFileName().c_str());
}

bool DeskNotes::FillRichEditFromFile(HWND hWnd, LPCTSTR pszFile)
{
	bool fSuccess = false;
	HANDLE hFile = CreateFile(pszFile, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE) 
	{
		EDITSTREAM es;
		es.pfnCallback = EditStreamCallbackRead;
		es.dwCookie = (DWORD_PTR)hFile;
		LRESULT lr = SendMessage(hWnd, EM_STREAMIN, SF_RTF, (LPARAM)&es);
		if (!es.dwError) 
			fSuccess = true;

		CloseHandle(hFile);
	}
	return fSuccess;
}

bool DeskNotes::SaveRichEditToFile(HWND hWnd, LPCTSTR pszFile)
{
	bool fSuccess = false;
	HANDLE hFile = CreateFile(pszFile, GENERIC_WRITE, 0, 0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE) 
	{
		EDITSTREAM es;
		es.pfnCallback = EditStreamCallbackWrite;
		es.dwCookie = (DWORD_PTR)hFile;
		LRESULT lr = SendMessage(hWnd, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
		if (!es.dwError) 
			fSuccess = true;

		CloseHandle(hFile);
	}
	return fSuccess;
}

void DeskNotes::Shrink(bool value)
{
	if (value && !shrunken)
	{
		restoredHeight = GetSize().cy;

		EnableWindow(editHWnd, FALSE);
		this->SetSize(this->GetSize().cx, GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CYFRAME) * 2, true);
		ModifyMenu(systemMenu, MenuSystem_ShrinkRestore, MF_STRING | MF_BYCOMMAND, MenuSystem_ShrinkRestore, Translate(TEXT("Restore")).c_str());
		
		shrunken = true;
	}
	else if (!value && shrunken)
	{
		shrunken = false;

		this->SetSize(GetSize().cx, restoredHeight, false);
		EnableWindow(editHWnd, TRUE);
		ModifyMenu(systemMenu, MenuSystem_ShrinkRestore, MF_STRING | MF_BYCOMMAND, MenuSystem_ShrinkRestore, Translate(TEXT("Shrink")).c_str());
	}

	Utilities::MoveWindowIntoVisibleArea(this->GetHWnd());
}

void DeskNotes::LoadRichEditConfiguration()
{
	Settings settings;
	Settings::Data data={0};
	data.DeskNotesData.ZoomNumerator = 0;
	data.DeskNotesData.ZoomDenominator = 0;
	data.DeskNotesData.ScrollPosition.x = 0;
	data.DeskNotesData.ScrollPosition.y = 0;
	settings.Load(&data);

	SendMessage(editHWnd, EM_SETZOOM, data.DeskNotesData.ZoomNumerator, data.DeskNotesData.ZoomDenominator);
	SendMessage(editHWnd, EM_SETSCROLLPOS, 0, (LPARAM)&data.DeskNotesData.ScrollPosition);
}


DWORD CALLBACK DeskNotes::EditStreamCallbackRead(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
{
	HANDLE hFile = (HANDLE)dwCookie;
	if (ReadFile(hFile, lpBuff, cb, (DWORD *)pcb, 0)) 
	{
		return 0;
	}
	return -1;
}

DWORD CALLBACK DeskNotes::EditStreamCallbackWrite(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
{
	HANDLE hFile = (HANDLE)dwCookie;
	if (WriteFile(hFile, lpBuff, cb, (DWORD *)pcb, 0)) 
	{
		return 0;
	}
	return -1;
}

LRESULT CALLBACK DeskNotes::WndProc(LPVOID sender, HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{		
	static RECT windowRectEnterSizeMove;

	DeskNotes* instance = (DeskNotes*)instances[hWnd];

	//static CHARRANGE tmpChr[2] = {0};

	switch (uMsg)
	{
	case WM_KEYDOWN:
		{
			if (wParam == 'S' && GetKeyState(VK_CONTROL) & 0x80)
				instance->SaveDeskNote();

			break;
		}
	case WM_ACTIVATE:
		{
			if (wParam == WA_INACTIVE)
			{
				if (!instance->topMost)
					SetWindowPos(hWnd, Utilities::GetLastValidTopLevelWindow(), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

				if (!instance->shrunken && instance->transparentOnLeave)
					SetLayeredWindowAttributes(hWnd, 0, 120, LWA_ALPHA);
			}
			else
			{
				SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
			}

			break;
		}
	case WM_DWMCOMPOSITIONCHANGED:
		{
			DwmIsCompositionEnabled(&dwmEnabled);

			if (dwmEnabled)
			{
				instance->roundCornerValue=15;
				MARGINS margins = {instance->dwmMarginSize,instance->dwmMarginSize,instance->dwmMarginSize,instance->dwmMarginSize};
				DwmExtendFrameIntoClientArea(hWnd,&margins);
			}
			else
			{
				instance->roundCornerValue=0;
			}

			break;
		}
	case WM_HOTKEY:
		{
			if (wParam == 1)
			{
				Utilities::MoveWindowIntoVisibleArea(hWnd);
				SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
				SetForegroundWindow(hWnd);
				SetFocus(instance->editHWnd);
			}

			break;
		}
	case WM_NOTIFY:
		{		
			static bool selectionChanged = false;
			NMHDR* nmhdr = (NMHDR*)lParam;

			if (nmhdr->code == EN_MSGFILTER)
			{
				MSGFILTER* pMsgFilter = (MSGFILTER*)nmhdr;

				if (pMsgFilter->msg == WM_KEYDOWN && pMsgFilter->wParam == 'S' && GetKeyState(VK_CONTROL) & 0x80)
					instance->SaveDeskNote();
			} 			
			else if (nmhdr->code == EN_LINK)
			{
				ENLINK* enl = (ENLINK*)nmhdr;
				if (enl->msg == WM_LBUTTONUP)
				{
					if (!selectionChanged)
					{
						TCHAR link[1024] = {0};
						TEXTRANGE textRange = {0};
						textRange.chrg = enl->chrg;
						textRange.lpstrText = link;
						SendMessage(enl->nmhdr.hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
						ShellExecute(0, 0, link, 0, 0, SW_SHOWNORMAL);
					}
				}
			} 		
			else if (nmhdr->code == EN_SELCHANGE)
			{
				SELCHANGE* sc = (SELCHANGE*)nmhdr;
				selectionChanged = (sc->seltyp != SEL_EMPTY);
			}
			else if (nmhdr->code == EN_DRAGDROPDONE)
			{
				printf("DragDrop done\n");
			}
			/*else if (nmhdr->code == EN_SELCHANGE)
			{
				SELCHANGE* sc = (SELCHANGE*)nmhdr;
				printf("EN_SELCHANGE\n");

				if (sc->seltyp != SEL_EMPTY)
				{
					static int counter = 0;

					if (!counter)
					{
						printf("Zuweisen von Sel 1\n");
						tmpChr[0] = sc->chrg;
					}
					else
					{
						printf("Zuweisen von Sel 2\n");
						tmpChr[1] = sc->chrg;
						counter = 0;
					}

					counter++;
				}
				else
				{
					printf("Reset von Sel\n");
					tmpChr[0].cpMax = -1;
					tmpChr[1].cpMax = -1;
					tmpChr[0].cpMin = -1;
					tmpChr[1].cpMin = -1;
				}
			}*/
			//else if (nmhdr->code == EN_SELCHANGE)
			//{
			//	SELCHANGE* sc = (SELCHANGE*)nmhdr;
			//	
			//	static int count = 0;
			//	static int pos = 8;
			//	static bool caretAtBookmark = false;

			//	if (!count)
			//	{
			//		GETTEXTLENGTHEX gtl = {0};
			//		gtl.codepage = 1200;
			//		gtl.flags = GTL_DEFAULT;
			//		count = (int)SendMessage(sc->nmhdr.hwndFrom, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
			//	}

			//	if (sc->seltyp == SEL_EMPTY && (sc->chrg.cpMax <= pos || (caretAtBookmark && sc->chrg.cpMax <= (pos + 1))))
			//	{		
			//		GETTEXTLENGTHEX gtl = {0};
			//		gtl.codepage = 1200;
			//		gtl.flags = GTL_DEFAULT;
			//		int newCount = (int)SendMessage(sc->nmhdr.hwndFrom, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
			//		
			//		if (newCount > count)
			//			pos += newCount - count;
			//		else if (newCount < count)
			//			pos -= count - newCount;

			//		if (sc->chrg.cpMax == pos)
			//			caretAtBookmark = true;

			//		if (newCount != count)
			//		{
			//			//pos has changed
			//			printf("Pos ist nun %d\n", pos);
			//			LRESULT line = SendMessage(sc->nmhdr.hwndFrom, EM_EXLINEFROMCHAR, 0, pos);
			//			printf("Zeile: %d\n", line);
			//		}
			//	}
			//	else if (caretAtBookmark)
			//	{
			//		caretAtBookmark = false;		
			//	}

			//	GETTEXTLENGTHEX gtl = {0};
			//	gtl.codepage = 1200;
			//	gtl.flags = GTL_DEFAULT;
			//	count = (int)SendMessage(sc->nmhdr.hwndFrom, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
			//}
			//else
			//{
			//	static bool bookmarkActive = true;
			//	static int pos = 8;
			//	static int count = 0;

			//	if (bookmarkActive)
			//	{
			//		MSGFILTER* pMsgFilter = (MSGFILTER*)nmhdr; 

			//		if (!count)
			//			count = Edit_GetTextLength(pMsgFilter->nmhdr.hwndFrom);

			//		int start = 0;
			//		int end = 0;
			//		LRESULT ret = SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);

			//		if (start == end)
			//		{
			//			int curPos = start;

			//			static int downCounter = 0;
			//			static int startPosition = -1;
			//			
			//			if (pMsgFilter->msg == WM_KEYDOWN)
			//			{
			//				if (startPosition == -1)
			//					startPosition = curPos;

			//				if (startPosition <= pos)
			//				{							
			//					downCounter++;
			//				}
			//			}
			//			if (pMsgFilter->msg == WM_KEYUP)
			//			{
			//				/*SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_SETSEL, 40, 40);
			//				SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_SCROLLCARET, 0, 0);*/
			//				
			//				if (startPosition <= pos)
			//				{
			//					bool changed = false;
			//					if (Edit_GetTextLength(pMsgFilter->nmhdr.hwndFrom) > count)
			//					{
			//						pos+=downCounter;
			//						changed = true;
			//					}
			//					else if (Edit_GetTextLength(pMsgFilter->nmhdr.hwndFrom) < count)
			//					{
			//						pos-=downCounter;
			//						changed = true;
			//					}

			//					if (changed)
			//					{
			//						if (pos < 0)
			//						{
			//							printf("Bookmark deleted\n");
			//							bookmarkActive = false;
			//						}
			//						else
			//						{
			//							printf("Pos ist nun %d\n", pos);
			//							LRESULT line = SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_EXLINEFROMCHAR, 0, pos);
			//							printf("Zeile: %d\n", line);
			//						}
			//					}
			//				}

			//				count = Edit_GetTextLength(pMsgFilter->nmhdr.hwndFrom);
			//				downCounter = 0;
			//				startPosition = -1;
			//			}
			//		}
			//		/*if (pMsgFilter->msg == WM_KEYUP && pMsgFilter->wParam == VK_RETURN)
			//		{
			//		printf("Enter KeyUp\n");
			//		printf("Position: %d\n", curPos);

			//		LRESULT line = SendMessage(pMsgFilter->nmhdr.hwndFrom, EM_EXLINEFROMCHAR, 0, curPos);
			//		printf("Zeile: %d\n", line);
			//		}*/
			//	}
			//}

			break;
		}		
	case WM_COMMAND:
		{
			if (HIWORD(wParam) == EN_UPDATE)
			{
				printf("EN_UPDATE\n");

				int start = 0;
				int end = 0;
				LRESULT ret = SendMessage((HWND)lParam, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
				printf("\tSelection:\tStart: %d\tEnd: %d\n", start, end);
			}
			if (HIWORD(wParam) == EN_CHANGE)
			{
				static int count = 0;
				static int pos = 8;

				if (!count)
				{
					GETTEXTLENGTHEX gtl = {0};
					gtl.codepage = 1200;
					gtl.flags = GTL_DEFAULT;
					count = (int)SendMessage((HWND)lParam, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
				}
				
				printf("Change\n");
				int start = 0;
				int end = 0;
				LRESULT ret = SendMessage((HWND)lParam, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
				printf("\tCaret: %d\n", end);

				GETTEXTLENGTHEX gtl = {0};
				gtl.codepage = 1200;
				gtl.flags = GTL_DEFAULT;
				int newCount = (int)SendMessage((HWND)lParam, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);

				/*if (tmpChr[0].cpMax != -1 && tmpChr[0].cpMin != -1 && start != end)
				{
					if (tmpChr[0].cpMax <= (pos + (tmpChr[0].cpMax - tmpChr[0].cpMin)))
					{
						printf("\tbetrifft Bookmark.\n");
						pos -= (tmpChr[0].cpMax - tmpChr[0].cpMin);

						printf("\tNeue Position: %d\n", pos);
						LRESULT line = SendMessage((HWND)lParam, EM_EXLINEFROMCHAR, 0, pos);
						printf("\tin Zeile: %d\n", line);
					}
					if (tmpChr[1].cpMax <= (pos + (tmpChr[1].cpMax - tmpChr[1].cpMin)))
					{
						printf("\tbetrifft Bookmark.\n");
						pos += (tmpChr[1].cpMax - tmpChr[1].cpMin);

						printf("\tNeue Position: %d\n", pos);
						LRESULT line = SendMessage((HWND)lParam, EM_EXLINEFROMCHAR, 0, pos);
						printf("\tin Zeile: %d\n", line);
					}
				}
				else*/
				{	
					int difference = newCount - count;
					printf("\tDifference: %d\n", difference);

					if (end <= (pos + difference))
					{
						printf("\tbetrifft Bookmark.\n");
						pos += difference;			

						printf("\tNeue Position: %d\n", pos);
						LRESULT line = SendMessage((HWND)lParam, EM_EXLINEFROMCHAR, 0, pos);
						printf("\tin Zeile: %d\n", line);
					}
				}

				count = newCount;
			}
			else
			{
				switch (LOWORD(wParam))
				{
				case MenuEdit_Save:
					{
						instance->SaveDeskNote();
						break;
					}
				case MenuEdit_ResetZoom:
					{
						SendMessage(instance->editHWnd, EM_SETZOOM, 0, 0);
						break;
					}
				case MenuSystem_ShrinkRestore:
					{
						instance->Shrink(!instance->shrunken);
						break;
					}
				case MenuSystem_TopMost:
					{
						instance->SetTopMost(!instance->topMost);
						instance->topMost = !instance->topMost;
						CheckMenuItem(instance->systemMenu, MenuSystem_TopMost, instance->topMost ? MF_CHECKED : MF_UNCHECKED);
					
						break;
					}
				case MenuSystem_TransparentOnLeave:
					{
						instance->transparentOnLeave = !instance->transparentOnLeave;
						CheckMenuItem(instance->systemMenu, MenuSystem_TransparentOnLeave, instance->transparentOnLeave ? MF_CHECKED : MF_UNCHECKED);
						
						break;
					}
				}
			}

			break;
		}
	case WM_CONTEXTMENU:
		{
			TITLEBARINFO ti = {0};
			ti.cbSize = sizeof(TITLEBARINFO);
			GetTitleBarInfo(hWnd, &ti);

			POINT mousePos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};

			if (mousePos.x == -1 && mousePos.y == -1)
			{
				if (instance->shrunken)
				{
					POINT pos = {instance->GetLocation().x + instance->GetSize().cx / 4, instance->GetLocation().y};
					TrackPopupMenu(instance->systemMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, pos.x, pos.y, 0, hWnd, 0);
				}
				else
				{
					POINT pos = {instance->GetLocation().x + instance->GetSize().cx / 2, instance->GetLocation().y + instance->GetSize().cy / 2};
					TrackPopupMenu(instance->editMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, pos.x, pos.y, 0, hWnd, 0);
				}
			}
			else if (Utilities::RectContainsPoint(ti.rcTitleBar, mousePos))
			{
				TrackPopupMenu(instance->systemMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, mousePos.x, mousePos.y, 0, hWnd, 0);
			}
			else
			{
				TrackPopupMenu(instance->editMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, mousePos.x, mousePos.y, 0, hWnd, 0);
			}
			break;
		}
	case WM_SETCURSOR:
		{
			if (HIWORD(lParam) == WM_MBUTTONDOWN && LOWORD(lParam) == HTCAPTION)
			{
				instance->Shrink(!instance->shrunken);
			}
			//replaced by WM_CONTEXTMENU
			/*else if (HIWORD(lParam) == WM_RBUTTONDOWN)
			{
				POINT cursorPos = Utilities::GetCursorPosition();

				if (LOWORD(lParam) == HTCAPTION)
				{
					TrackPopupMenu(instance->systemMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, cursorPos.x, cursorPos.y, 0, hWnd, 0);
					PostMessage(hWnd, WM_RBUTTONUP, 0, 0);
				}
				else if (LOWORD(lParam) == HTCLIENT)
				{
					TrackPopupMenu(instance->editMenu, TPM_VERNEGANIMATION | TPM_LEFTBUTTON, cursorPos.x, cursorPos.y, 0, hWnd, 0);
				}
			}*/

			if (!instance->shrunken)
				break;

			if (LOWORD(lParam) == HTTOP
				|| LOWORD(lParam) == HTBOTTOM
				|| LOWORD(lParam) == HTTOPLEFT
				|| LOWORD(lParam) == HTBOTTOMLEFT
				|| LOWORD(lParam) == HTLEFT
				|| LOWORD(lParam) == HTRIGHT
				|| LOWORD(lParam) == HTTOPRIGHT
				|| LOWORD(lParam) == HTBOTTOMRIGHT)
			{
				lParam = MAKELPARAM(HTNOWHERE, HIWORD(lParam));
			}

			break;
		}
	case WM_NCLBUTTONDOWN:
		{
			if (!instance->shrunken)
				break;

			if (wParam == HTTOPLEFT
				|| wParam == HTBOTTOMLEFT
				|| wParam ==  HTTOPRIGHT
				|| wParam ==  HTBOTTOMRIGHT
				|| wParam ==  HTBOTTOM
				|| wParam ==  HTTOP
				|| wParam ==  HTLEFT
				|| wParam ==  HTRIGHT)
			{
				wParam = HTCAPTION;
			}

			break;
		}
	case WM_WINDOWPOSCHANGING:
		{
			if (!instance->shrunken)
				break;

			WINDOWPOS* wp = (WINDOWPOS*)lParam;
			wp->cy = GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CYFRAME) * 2;
			lParam = (LPARAM)wp;
			return S_OK;

		}
	case WM_PAINT:
		{
			SolidBrush	 whiteBrush(Color(255, 255, 255));
			Pen			 borderPen(Color(120, 120, 120));
			Pen			 secondaryBorderPen(Color(200, 200, 200));
			HDC          hdc;
			PAINTSTRUCT  ps;
			RECT		 clientRect;

			GetClientRect(hWnd,&clientRect);

			hdc = BeginPaint(hWnd, &ps);

			Graphics graphics(hdc);
			graphics.SetSmoothingMode(SmoothingModeAntiAlias);

			GraphicsPath fillPath;
			GraphicsPath secondaryBorderPath;

			Utilities::CreateRoundRectPath(&fillPath, clientRect, instance->roundCornerValue, -1);
			Utilities::CreateRoundRectPath(&secondaryBorderPath, clientRect, instance->roundCornerValue);

			graphics.FillPath(&whiteBrush,&fillPath);
			graphics.DrawPath(&borderPen,&fillPath);
			graphics.DrawPath(&secondaryBorderPen,&secondaryBorderPath);

			EndPaint(hWnd, &ps);
			break;
		}
	case WM_SIZE:
		{
			RECT clientRect;
			GetClientRect(hWnd,&clientRect);

			SetWindowPos(instance->editHWnd, 0, 0, 0, clientRect.right - clientRect.left - 2 * instance->dwmMarginSize, clientRect.bottom - clientRect.top - 2 * instance->dwmMarginSize, SWP_NOMOVE);

			break;
		}		
	case WM_GETMINMAXINFO:
		{
			if (instances.find(hWnd) != instances.end())
			{
				MINMAXINFO * mmi = (MINMAXINFO*)lParam;
				mmi->ptMinTrackSize.x = instance->minWidth;
				mmi->ptMinTrackSize.y = instance->minHeight;
			}
			return S_OK;
		}
	case WM_ENTERSIZEMOVE:
		{
			GetWindowRect(hWnd,&windowRectEnterSizeMove);
			SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,0);
			break;
		}
	case WM_EXITSIZEMOVE:
		{
			RECT rect;
			GetWindowRect(hWnd,&rect);

			SetClassLongPtr(hWnd,GCL_HBRBACKGROUND,(LONG_PTR)GetStockObject(BLACK_BRUSH));
				
			if ((rect.right-rect.left) != (windowRectEnterSizeMove.right-windowRectEnterSizeMove.left) || (rect.bottom-rect.top) != (windowRectEnterSizeMove.bottom-windowRectEnterSizeMove.top))
				InvalidateRgn(hWnd,0,true);

			break;
		}
	case WM_DISPLAYCHANGE:
        {
            instance->Refresh();
            break;
        }
	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

VOID CALLBACK DeskNotes::TimerProc(HWND hWnd,UINT uMsg,UINT_PTR	idEvent,DWORD dwTime)
{
	((DeskNotes*)instances[hWnd])->SaveDeskNote();
}
